home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 37 / Amiga Format CD37 (1999-02-16)(Future Publishing)(GB)(Track 1 of 3)[!][issue 1999-03].iso / -screenplay- / shareware / invasionforce / source / cyber_order.c < prev    next >
C/C++ Source or Header  |  1999-01-09  |  16KB  |  523 lines

  1. /*
  2. AI Code for Invasion Force - an Explore/Conquer Strategic Wargame
  3. Copyright (C) 1996  Brannen Hough
  4.  
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. */
  19. /*
  20.     cyber_order.c -- artificial intelligence module for Empire II
  21.  
  22. */
  23.  
  24. /* This file contains all the routines associated with AI order execution.
  25. */
  26.  
  27.  
  28. #include "global.h"
  29. extern short Type;
  30. void MoveUnitDir( struct Unit*, enum Direction );
  31.  
  32. int
  33. DoUnitActions( int HighOdds, int LowOdds )
  34. {
  35.     struct Unit *unit = (struct Unit *)unit_list.mlh_Head;
  36.     int         Done = TRUE;
  37.     static struct Unit* lastunit = NULL;
  38.     static int    times_accessed = 0;
  39.     int           attack_dir;
  40.     char          my_buf[120];
  41.     char          play_buf[40] = "";
  42.     char          name_buf[60] = "";
  43.     char          ord_buf[60] = "";
  44.     char          ord_buf2[20] = "";
  45.  
  46.     for ( ; unit->unode.mln_Succ; unit = (struct Unit *) unit->unode.mln_Succ)
  47.         if ((unit->owner==player) && (unit->move > 0)) {
  48.         /* Added this last to only give any one unit 10 chances to
  49.            move - if it hasn't by then, skip over it and go to the
  50.            next.  This has the pleasant effect that the unit that
  51.            couldn't move in 10 tries will get another 10 tries to
  52.            move, and so on, until it finally gets skipped for this
  53.            turn. */
  54.         if ( (lastunit != unit) || (times_accessed < 10) )  {
  55.             if( lastunit != unit ) {
  56.                 lastunit = unit;
  57.                 times_accessed = 0;
  58.             }
  59.             else {
  60.                 times_accessed++;
  61.             }
  62.             /* Skip Sentry units */
  63.             if ((unit->orders != NULL) && 
  64.                 (unit->orders->reserved == C_ORDER_SENTRY))   
  65.                 continue;
  66.  
  67.             // AI information
  68.             switch( AIDataFlag ) {
  69.             case 5:
  70.               // Scroll display to unit and show it
  71.               if (need_to_scrollP(unit->col,unit->row)) {
  72.                   int ox=xoffs, oy=yoffs;
  73.                   set_display_offsets(unit->col,unit->row);
  74.                   GP_smart_scroll(ox,oy);
  75.               }
  76.               start_blinking_unit(unit);
  77.               //plot_icon(unit->type,unit->col,unit->row,
  78.               //    roster[unit->owner].color, ORDER_NONE, 0);
  79.               unit_status_bar(unit);
  80.               //  A deliberate fall-through
  81.             case 4:
  82.               // Add orders
  83.               if( unit->orders ) {
  84.             switch( unit->orders->reserved ) {
  85.               case C_ORDER_GOTO:
  86.                 sprintf( ord_buf, "- Orders: Goto %ld,%ld",
  87.                  unit->orders->destx, unit->orders->desty);
  88.                 break;
  89.               case  C_ORDER_RANDOM:
  90.                 sprintf( ord_buf, "- Orders: Go Random" );
  91.                 break;
  92.               case  C_ORDER_HUNT:
  93.                 sprintf( ord_buf, "- Orders: Hunting" );
  94.                 break;
  95.               case  C_ORDER_RECON:
  96.                 sprintf( ord_buf, "- Orders: Recon to %ld, %ld",
  97.                  unit->orders->destx, unit->orders->desty);
  98.                 break;
  99.               case  C_ORDER_WALK_COASTLINE:
  100.                 sprintf( ord_buf, "- Orders: Walk Coastline" );
  101.                 break;
  102.             }
  103.               } // End if tell orders too
  104.               if( (unit->type == FIGHTER) ||
  105.               (unit->type == BOMBER) ||
  106.               (unit->type == AIRCAV) ) {
  107.               sprintf( ord_buf2, "- Fuel: %ld", unit->fuel );
  108.               strcat( ord_buf, ord_buf2 );
  109.               }
  110.               // deliberate fall-through
  111.             case 3:
  112.               // Add unit types and names
  113.               if( unit->name ) {
  114.             sprintf( name_buf, "%s %s", 
  115.                  UnitString[unit->type],unit->name );
  116.               }
  117.               else {
  118.             sprintf( name_buf, "a %s",
  119.                  UnitString[unit->type] );
  120.               }
  121.               // The general information
  122.               sprintf( play_buf, 
  123.                    "Player %ld (AI type %ld)", 
  124.                    player, PLAYER.aggr );
  125.               // print out the message
  126.               sprintf( my_buf, "%s %s %s", play_buf, name_buf, 
  127.                    ord_buf );
  128.               // Show the unit being processed.
  129.               tell_user2( my_buf, FALSE, NULL );
  130.               break;
  131.             case 2:
  132.               // Do Nothing
  133.               // Deliberate fall-through
  134.             case 1:
  135.               // Do Nothing
  136.               // Deliberate fall-through
  137.             default: // 0
  138.               // Do nothing
  139.               break;
  140.             } // End switch
  141.  
  142.             /* Take a look around us and react to enemy units + cities */
  143.             /* New for this version - let's use the recommend action
  144.                routine. */
  145.             attack_dir = AI5_recommend_action(unit,HighOdds,LowOdds);
  146.             if( attack_dir != -1 ) {
  147.                 MoveUnitDir( unit, attack_dir );
  148.                 return (FALSE);
  149.             }
  150.             /* if we have orders, execute them */
  151.             if (unit->orders != NULL) {
  152.                 ExecuteStandingOrder(unit);
  153.                 return (FALSE);
  154.             }
  155.             else {
  156.                 DEBUG_AI("No orders, no enemy around to attack.")
  157.                 // We have no orders, so have the AI make some 
  158.                 // new orders
  159.                 AIAddLib( unit );
  160.                 return (FALSE);
  161.              } /* End else figure something out */
  162.         } /* End if the unit is not the same one we accessed 10 times */
  163.         else {
  164.             /* Last unit IS this unit and times accessed >= 10. */
  165.             /* This should ensure that we never get stuck forever - we
  166.                 will always finish - removes a potential infinite loop
  167.                 when there are TWO units without any moves.  Without
  168.                 this the loop will ping pong between them forever. */
  169.             unit->move -= 60;
  170.             if( unit->move < 0 ) unit->move = 0;
  171.             
  172.             if( (unit->type==FIGHTER) || 
  173.             (unit->type==BOMBER) ||
  174.             (unit->type==AIRCAV) ) {
  175.                 unit->fuel--;
  176.                 if( unit->fuel <= 0 ) Remove ((struct Node*)unit);
  177.             }          
  178.             times_accessed = 0;
  179.             lastunit = NULL;
  180.         }
  181.     } /* End if we own unit and it has moves left */
  182.     /* End for loop */    
  183.     times_accessed = 0;
  184.     lastunit = NULL;
  185.     return (Done);
  186. }
  187.  
  188.  
  189. void
  190. ExecuteStandingOrder( struct Unit *unit )
  191. {
  192.     if ((unit->orders == NULL) || (unit->orders->type != ORDER_NONE)) {
  193.     DEBUG_AI("Big problem in execute_standing_order - no orders!")
  194.     sprintf (outbuf, "%s %s has no standing orders to execute - aborting",
  195.          UnitString[unit->type], unit->name);
  196.     DEBUG_AI(outbuf)
  197.     return;
  198.     }
  199.     
  200.     switch (unit->orders->reserved) {
  201.         case C_ORDER_GOTO:
  202.             CommandGoto(unit);
  203.         break;
  204.         case  C_ORDER_RANDOM:
  205.             CommandRandom(unit);
  206.             break;
  207.         case  C_ORDER_HUNT:
  208.             CommandHunt(unit);
  209.         break;
  210.         case  C_ORDER_RECON:
  211.             CommandRecon(unit);
  212.             break;
  213.         case  C_ORDER_WALK_COASTLINE:
  214.         CommandWalkCoastline( unit );
  215.         break;
  216.         default:
  217.             DEBUG_AI("Unknown command type found in execute_standing_order!")
  218.             break;
  219.     }
  220.     return;
  221. }
  222.  
  223.  
  224. void
  225. CommandGoto( struct Unit* unit )
  226. {
  227.   // Check the orders for the destination
  228.   if( (unit->col == unit->orders->destx) && 
  229.       (unit->row == unit->orders->desty) ) {
  230.       // Clear the orders
  231.       clear_orders( unit );
  232.   }
  233.   else {
  234.       // If we are not at the destination, can we get there?
  235.       AI5_CalcPath( unit->type, unit->col, unit->row, unit->orders->destx,
  236.             unit->orders->desty, AI5_PATH_GOOD );
  237.       if( Path[0] >= 0 ) {
  238.       // Check aircraft for fuel
  239.       if( (wishbook[ unit->type ].range > 0) && PathLength > unit->fuel ) {
  240.           // We have an aircraft and it can't make it
  241.           clear_orders( unit );
  242.       }
  243.       else {
  244.           // If so, take the first step.
  245.           MoveUnitDir( unit, Path[0] );
  246.           return;
  247.       }
  248.       }
  249.       else {
  250.       clear_orders( unit );
  251.       }
  252.   }
  253.   return;
  254. }
  255.  
  256.  
  257. void
  258. CommandRandom( struct Unit* unit )
  259. {
  260.   // Check that we have a space we can move into ahead of us
  261.   short targx, targy;
  262.   int newrand;
  263.   int ret = AI1_calc_dir( (enum Direction)unit->orders->etc, unit->col, 
  264.               unit->row, &targx, &targy);
  265.   if( ret != -1 ) {
  266.     Type = unit->type;  // Need to use this global variable to talk to
  267.                         //  AI5_GetCost
  268.     // The space exists on the map, can we move there?
  269.     if( AI5_GetCost(targx, targy) > 0 ) {
  270.       // Make the move
  271.       MoveUnitDir( unit, (enum Direction)unit->orders->etc );
  272.       return;
  273.     }
  274.   }
  275.   // If we are here, we couldn't move in that direction
  276.   newrand = unit->orders->etc;
  277.   // if we can't move, pick a different direction
  278.   while( newrand == unit->orders->etc ) {
  279.     newrand = (int) RangeRand( 6L );
  280.   }
  281.   unit->orders->etc = newrand;
  282. }
  283.  
  284.  
  285. void
  286. CommandHunt( struct Unit* unit )
  287. {
  288.   // We need to find the closest enemy unit within unit->orders->etc
  289.   //   of this unit that we can reach, then take a step towards him
  290.   struct MapIcon* closest = FindClosestEnemyIcon( unit, unit->orders->etc );
  291.   if( closest ) {
  292.     // recalculate a path to get there
  293.     AI5_CalcPath( unit->type, unit->col, unit->row, closest->col,
  294.           closest->row, AI5_PATH_GOOD );
  295.     if( Path[0] != -1 ) {
  296.       // Take a step
  297.       MoveUnitDir( unit, Path[0] );
  298.       return;
  299.     }
  300.   }
  301.   // Can't find anyone to hunt!
  302.   clear_orders( unit );
  303.   return;
  304. }
  305.  
  306. struct MapIcon* FindClosestEnemyIcon( struct Unit* unit, int limit )
  307. {
  308.     int  closest = BIG_NUMBER;
  309.     int  i;
  310.     struct MapIcon *closestEnemy = NULL;
  311.     struct MapIcon *icon = (struct MapIcon *) PLAYER.icons.mlh_Head;
  312.  
  313.     for (; icon->inode.mln_Succ; icon = (struct MapIcon *) 
  314.       icon->inode.mln_Succ) {
  315.         if( (icon->owner != player) && (icon->type != CITY) ) {
  316.         i = AI5_GetDist( unit->col, unit->row, icon->col, icon->row );
  317.         // Check on a path to get there
  318.         AI5_CalcPath( unit->type, unit->col, unit->row, icon->col,
  319.               icon->row, AI5_PATH_GOOD );
  320.         if( (i < limit) && (i < closest) && (Path[0] != -1) ) {
  321.         closest = i;
  322.         closestEnemy = icon;
  323.         }
  324.     }
  325.     }
  326.     return( closestEnemy );
  327. }
  328.  
  329.  
  330. void
  331. CommandRecon( struct Unit* unit )
  332. {
  333.     /* With this order we go out to the destination, then, once we
  334.        reach it or hit 1/2 fuel, replace the destination with the
  335.        origin (home) and head for it.
  336.        */
  337.     /* Very first thing is to check that if we are on the last half
  338.        of our fuel, we are headed home.  If not, start heading home.
  339.        */
  340.     if( (wishbook[unit->type].range > 0) && 
  341.     (unit->fuel <= wishbook[unit->type].range / 2) &&
  342.     ((unit->orders->destx != unit->orders->orgx) ||
  343.      (unit->orders->desty != unit->orders->orgy)) ) {
  344.           unit->orders->destx = unit->orders->orgx;
  345.           unit->orders->desty = unit->orders->orgy;
  346.           unit->orders->etc = unit->fuel;
  347.     }
  348.     // Have we reached the destination?
  349.     if( (unit->orders->destx == unit->col) && 
  350.     (unit->orders->desty == unit->row) ) {
  351.       // We have reached the destination, but is it the final one?
  352.       // If so, clear orders and return, otherwise, put in the new
  353.       //    destination for our second leg
  354.       if( (unit->orders->destx != unit->orders->orgx) ||
  355.       (unit->orders->desty != unit->orders->orgy) ) {
  356.           unit->orders->destx = unit->orders->orgx;
  357.           unit->orders->desty = unit->orders->orgy;
  358.           unit->orders->etc = unit->fuel;
  359.       }
  360.       else {
  361.       clear_orders( unit );
  362.       return;
  363.       }
  364.     }
  365.     // Take a step towards our goal
  366.     AI5_CalcPath( unit->type, unit->col, unit->row, unit->orders->destx,
  367.           unit->orders->desty, AI5_PATH_GOOD );
  368.     if( Path[0] == -1 ) {
  369.         // Can't get there
  370.         clear_orders( unit );
  371.         return;
  372.     }
  373.     MoveUnitDir( unit, Path[0]);
  374. }
  375.  
  376.  
  377.  
  378. void
  379. CommandWalkCoastline( struct Unit* unit )
  380. {
  381.     /* This command will cause the unit (hopefully a ground or naval
  382.        unit) to walk along the coastline, either clockwise (etc = 0)
  383.        or counterclockwise (etc != 0).  Useful for exploring.
  384.        Current direction to start from is kept in destx. The space 
  385.        we started from is kept in orgx, orgy - when we move and end
  386.        up back there we have completed the walk.
  387.        */
  388.     short targx, targy;
  389.     int   current;
  390.     int   new_dir = unit->orders->destx;
  391.     int   result;
  392.  
  393.     if( (unit->orders->destx < 0) || (unit->orders->destx > 5) ) {
  394.         clear_orders( unit );  /* Problem */
  395.     return;
  396.     }
  397.     // Now, figure out the next step to take
  398.     Type = unit->type; // Set a global to talk to the GetCost routine
  399.     current = AI1_calc_dir( new_dir, unit->col, unit->row, &targx, &targy);
  400.     while( ( current != -1) && ( AI5_GetCost( targx, targy ) > 0 ) ) {
  401.         if( unit->orders->etc )
  402.             new_dir++;
  403.         else
  404.             new_dir--;
  405.         /* Wrap around the directions */
  406.     if( new_dir > 5 ) new_dir = 0;
  407.         if( new_dir < 0 ) new_dir = 5;
  408.     /* Check that we haven't come full circle and gotten nowhere */
  409.         if( new_dir == unit->orders->destx )  {
  410.         clear_orders( unit );
  411.         return;
  412.     }
  413.     current = AI1_calc_dir( new_dir, unit->col, unit->row, &targx, &targy);
  414.     } // End while
  415.     // Record the new direction (or the old one if we didn't change it.
  416.     unit->orders->destx = new_dir;
  417.  
  418.     // And, finally, take the step
  419.     MoveUnitDir( unit, new_dir );
  420.     return;
  421. }
  422.  
  423.  
  424. void
  425. ComputerGiveOrders( struct Unit* unit, int suborder, short destx,
  426.             short desty, short orgx, short orgy, int etc )
  427. {
  428.    struct Order *order=AllocVec((long)sizeof(*order),MEMF_CLEAR);
  429.     
  430.    clear_orders(unit);
  431.    if (order) {
  432.       order->destx = destx;
  433.       order->desty = desty;
  434.       unit->orders = order;
  435.       order->type = ORDER_NONE;
  436.       if (orgx == -1)  order->orgx = unit->col;
  437.       else  order->orgx = orgx;
  438.       if (orgy == -1)  order->orgy = unit->row;
  439.       else  order->orgy = orgy;
  440.       order->processed = FALSE;
  441.       order->etc = etc;
  442.       order->reserved = suborder;       /* using reserved for computer
  443.                                   orders so I don't tread on the human
  444.                                   player's tokens. */
  445.                                         
  446.       /* This is set up to be in the order they are used most often,
  447.          and set up so that more order types can be added as needed.
  448.         */
  449.       switch (suborder) {
  450.       case C_ORDER_GOTO:
  451.         /* Standard stuff only */
  452.             break;
  453.       case C_ORDER_RECON:
  454.         order->etc = wishbook[unit->type].range / 2 - 1;
  455.         break;
  456.       case C_ORDER_RANDOM:
  457.         /* We need to make sure we have a valid initial direction */
  458.         if( (order->etc > 5) || (order->etc < 0) )
  459.           order->etc = (int) RangeRand( 6L );
  460.         break;
  461.       case C_ORDER_HUNT:
  462.         /* Standard stuff only */
  463.         break;
  464.       case C_ORDER_WALK_COASTLINE:
  465.         /* etc contains clockwise or counter-clockwise */
  466.         order->destx = 0; /* Initial direction */
  467.         order->orgx = unit->col;
  468.         order->orgy = unit->row;
  469.         break;
  470.       case C_ORDER_SENTRY:
  471.         /* Standard stuff only */
  472.         break;
  473.       }
  474.    }
  475. }
  476.  
  477.  
  478. void
  479. AIAddLib( struct Unit* unit )
  480. {
  481.     switch( PLAYER.aggr ) {
  482.       // Do not include cases for AI1 and AI2 - the default action is
  483.       //   enough
  484.       case 3: {
  485.         AI3_orders_for_unit( unit );
  486.         break;
  487.       }
  488.       case 4: {
  489.         AI4_orders_for_unit( unit );
  490.         break;
  491.       }
  492.       default: {
  493.         // This used to be in the DoUnitActions routine
  494.         /* Just skip this unit this turn - the 
  495.             governor will give new orders next turn. */
  496.         if( (unit->type==FIGHTER) || 
  497.             (unit->type==BOMBER) ||
  498.             (unit->type==AIRCAV) ) {
  499.             unit->fuel -= unit->move / 60;
  500.             unit->move = 0;
  501.             if( unit->fuel <= 0 ) Remove ((struct Node*)unit);
  502.             return;
  503.         }
  504.         unit->move = 0;
  505.         break;
  506.       }
  507.     }
  508. }
  509.  
  510.  
  511. void
  512. MoveUnitDir( struct Unit* unit, enum Direction dir )
  513. {
  514.     short x, y;
  515.     int result = AI1_calc_dir( dir, unit->col, unit->row, &x, &y );
  516.     if( AIDataFlag >= 5 ) {
  517.         restore_hex_graphics(unit->col,unit->row,0);        
  518.     }
  519.     move_unit_dir( unit, dir );
  520.     if( (result != -1) && (AIDataFlag >= 5) )
  521.         GP_update_hex_display( x, y );
  522. }
  523.